﻿using System;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

using Perseus.Plugins;

using Hetymine.Executioner.Commands;
using Hetymine.Executioner.Data;
using Hetymine.Executioner.Triggers;

namespace Hetymine.Executioner.Plugins.Triggers.Clipboard {
    public class Clipboard : TriggerPlugin, ILoader, IWindowHook {
        #region PInvoke
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SetClipboardViewer(IntPtr hWnd);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        private const int WM_DRAWCLIPBOARD = 0x0308;
        private const int WM_CHANGECBCHAIN = 0x030D;
        #endregion

        private static IntPtr _ClipboardViewerNext;
        private static IntPtr _Handle;

        private bool _IsSetting;

        public Clipboard() {
            this.PluginInfo = PluginInfo.FromAssembly(
                "Clipboard",
                this.GetType().Assembly,
                "Blake Hancock",
                "http://hetymine.com/executioner/plugins/trigger/clipboard/"
            );

            this._IsSetting = false;
        }

        public override ITrigger InitializeTrigger() {
            return new ClipboardTrigger();
        }
        #region IWindowsHook
        public bool HookHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            if (msg == WM_DRAWCLIPBOARD) {
                Clipboard.SendMessage(Clipboard._ClipboardViewerNext, msg, wParam, lParam);

                // In the case that an executed command sets the clipboards text, we dont want to recheck its pattern.
                if (!this._IsSetting && Executioner.Commands.ContainsKey("Trigger") && System.Windows.Clipboard.ContainsText()) {
                    this._IsSetting = true;

                    string data = System.Windows.Clipboard.GetText();

                    foreach (TriggerCommand tc in Executioner.Commands["Trigger"]) {
                        foreach (ITrigger t in tc.Triggers) {
                            if (t is ClipboardTrigger) {
                                var ct = t as ClipboardTrigger;
                                try {
                                    if (Regex.IsMatch(data, ct.Pattern)) {
                                        tc.Execute(string.Empty, true);
                                        break;
                                    }                                    
                                }
                                catch { }
                            }
                        }
                    }

                    this._IsSetting = false;
                }

                return true;
            }
            else if (msg == WM_CHANGECBCHAIN) {
                // If the handle to the window being removed is the next
                if (wParam == Clipboard._ClipboardViewerNext) {
                    // Update pointer to next
                    Clipboard._ClipboardViewerNext = lParam;
                }
                else {
                    Clipboard.SendMessage(Clipboard._ClipboardViewerNext, msg, wParam, lParam);
                }

                return true;
            }

            return false;
        }
        #endregion
        #region ILoader
        public void Load() {
            this._IsSetting = true;
            Clipboard._Handle = Executioner.Handle;
            Clipboard._ClipboardViewerNext = Clipboard.SetClipboardViewer(Clipboard._Handle);
            this._IsSetting = false;
        }
        public void Unload() {
            Clipboard.ChangeClipboardChain(Clipboard._Handle, Clipboard._ClipboardViewerNext);
        }
        #endregion        
    }
}
